﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;

namespace nbcs_chess_squad
{

    public class Network
    {

        #region Variables

        // Reference for the Chess main screen
        GameForm objChess = null;
     
        // Thread for client and server
        public Thread thread_receive_client;
        public Thread thread_receive_server;

        // Server IP and port
        private string wServerIP;
        const int SERVERPORT = 12790;

        // Loop control variables for client and server threads
        bool wReceivingServer = true;
        bool wReceivingClient = true;

        // TCP e NetworkStream objects for client and server
        NetworkStream clientSockStream;
        NetworkStream serverSockStream;

        TcpClient tcpClient;
        TcpListener tcpListener;
        Socket soTcpServer;

        #endregion

        #region Constructor

        public Network(GameForm GF)
        {
            objChess = GF;
        }

        #endregion

        #region Client

        public void ConnectServer(string pIP)
        {
            // Connect to a game server
            wServerIP = pIP;
            byte[] buf = new byte[1];

            thread_receive_client = new Thread(new ThreadStart(ThreadReceivingClient));
            thread_receive_client.Start();
        }


        private void ThreadReceivingClient()
        {
            // Thread for receiving packets from server
            try
            {

                byte[] buf = new byte[512];
                int bytesReceived = 0;

                tcpClient = new TcpClient(wServerIP, SERVERPORT);
                clientSockStream = tcpClient.GetStream();

                wReceivingClient = true;

                objChess.SetStatusMessage("Connected!");
                objChess.connected = true;
                objChess.playercolor = false;

                while (wReceivingClient)
                {
                    // Thread is blocked until receives data
                    try
                    {
                        bytesReceived = clientSockStream.Read(buf, 0, buf.Length);
                    }
                    catch
                    {
                        return;
                    }

                    // Processes network packet
                    if (bytesReceived > 0)
                    {
                        // Control packet for game restart

                        if (buf[0] == byte.Parse(Asc("R").ToString()))
                        {
                            objChess.RestartGame();
                            continue;
                        }

                        if (buf[0] == byte.Parse(Asc("D").ToString()))
                        {
                            objChess.btnConnectDisconnect_Click(null, null);
                            continue;
                        }

                        if (buf[0] == byte.Parse(Asc(".").ToString()))
                        {
                            string message = Encoding.ASCII.GetString(buf, 0, buf.Length);
                            message = message.Replace(".", "");

                            objChess.UpdateLog(message);
                        }

                        else
                        {
                            // Packet indicating a game move
                            int originX = int.Parse(Convert.ToChar(buf[0]).ToString());
                            int originY = int.Parse(Convert.ToChar(buf[1]).ToString());
                            int finalX = int.Parse(Convert.ToChar(buf[2]).ToString());
                            int finalY = int.Parse(Convert.ToChar(buf[3]).ToString());

                            objChess.wNetworkPlay = true;
                            objChess.MovePiece(originX, originY, finalX, finalY, true);
                        }
                    }

                }

            }


            catch (ThreadAbortException) { }
            catch (Exception ex)
            {
                MessageBox.Show("Could not locate host on specified IP Address", "No Host Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
                MessageBox.Show("An error ocurred: " + ex.Message + "\n" + ex.StackTrace);
                objChess.btnConnectDisconnect_Click(null, null);
                return;
            }

        }

        #endregion

        #region Server

        public void StartServer()
        {
            // Starts game server
            thread_receive_server = new Thread(new ThreadStart(ThreadReceivingServer));
            thread_receive_server.Start();
        }


        private void ThreadReceivingServer()
        {
            // Thread for receiving packets from client
            try
            {
                byte[] buf = new byte[512];
                IPHostEntry localHostEntry = Dns.GetHostByName(Dns.GetHostName());
                int bytesReceived = 0;

                tcpListener = new TcpListener(localHostEntry.AddressList[0], SERVERPORT);
                objChess.UpdatetxtIP(localHostEntry.AddressList[0].ToString());

                tcpListener.Start();

                // Thread is blocked until it gets a connection from client
                soTcpServer = tcpListener.AcceptSocket();

                serverSockStream = new NetworkStream(soTcpServer);

                objChess.RestartGame();
                objChess.SetStatusMessage("Connected!");
                objChess.connected = true;
                objChess.playercolor = true;

                wReceivingServer = true;

                while (wReceivingServer)
                {
                    // Thread is blocked until receives data
                    try
                    {
                        bytesReceived = serverSockStream.Read(buf, 0, buf.Length);
                    }
                    catch
                    {
                        return;
                    }

                    // Processes network packet
                    if (bytesReceived > 0)
                    {

                        // Control packet for game restart
                        if (buf[0] == byte.Parse(Asc("R").ToString()))
                        {
                            objChess.RestartGame();
                            continue;
                        }

                        if (buf[0] == byte.Parse(Asc(".").ToString()))
                        {
                            string message = Encoding.ASCII.GetString(buf, 0, buf.Length);
                            message = message.Replace(".", "");

                            objChess.UpdateLog(message);
                        }

                        else
                        {
                            // Packet indicating a game move
                            int originX = int.Parse(Convert.ToChar(buf[0]).ToString());
                            int originY = int.Parse(Convert.ToChar(buf[1]).ToString());
                            int finalX = int.Parse(Convert.ToChar(buf[2]).ToString());
                            int finalY = int.Parse(Convert.ToChar(buf[3]).ToString());

                            objChess.wNetworkPlay = true;
                            objChess.MovePiece(originX, originY, finalX, finalY, true);
                        }

                    }

                }
            }
            catch (ThreadAbortException) { }
            catch (Exception ex)
            {
                MessageBox.Show("An error ocurred: " + ex.Message + "\n" + ex.StackTrace);
                objChess.btnConnectDisconnect_Click(null, null);
                return;
            }
        }

        #endregion

        #region Functions for sending packets/disconnect

        public void SendPacketTCP(Byte[] pDados)
        {
            // Sends a packet via TCP
            try
            {
                if (objChess.wClient == true)
                {
                    if (clientSockStream == null)
                        return;

                    if (clientSockStream.CanWrite)
                    {
                        clientSockStream.Write(pDados, 0, pDados.Length);
                        clientSockStream.Flush();
                    }
                }
                else
                {
                    if (serverSockStream == null)
                        return;

                    if (serverSockStream.CanWrite)
                    {
                        serverSockStream.Write(pDados, 0, pDados.Length);
                        serverSockStream.Flush();
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("An error ocurred: " + ex.Message + "\n" + ex.StackTrace);
                objChess.btnConnectDisconnect_Click(null, null);
                return;
            }

        }

        public void SendMove(int originX, int originY, int finalX, int finalY)
        {
            // Sends packet that shows move position and the origin position
            byte[] buf = new byte[4];
            buf[0] = byte.Parse(Asc(originX.ToString()).ToString());
            buf[1] = byte.Parse(Asc(originY.ToString()).ToString());
            buf[2] = byte.Parse(Asc(finalX.ToString()).ToString());
            buf[3] = byte.Parse(Asc(finalY.ToString()).ToString());

            SendPacketTCP(buf);

        }

        public void SendMessage(string Message)
        {
            byte[] buf = Encoding.ASCII.GetBytes(Message);
            SendPacketTCP(buf);
        }

        public void SendsRestartPacket()
        {
            // Sends packet for the other game restart
            byte[] buf = new byte[2];
            buf[0] = byte.Parse(Asc("R").ToString());
            buf[1] = 0;

            SendPacketTCP(buf);
        }

        public void SendDisconnectPacket()
        {
            byte[] buf = new byte[2];
            buf[0] = byte.Parse(Asc("D").ToString());
            buf[1] = 0;

            SendPacketTCP(buf);
        }

        public void Disconnect()
        {
            // Disconnect client and server
            if (objChess.wClient == true)
            {
                thread_receive_client.Abort();

                wReceivingClient = false;

                if (clientSockStream != null)
                    clientSockStream.Close();

                if (tcpClient != null)
                    tcpClient.Close();

            }

            if (objChess.wServer == true)
            {
                thread_receive_server.Abort();

                wReceivingServer = false;

                if (serverSockStream != null)
                    serverSockStream.Close();

                if (tcpListener != null)
                    tcpListener.Stop();

                if (soTcpServer != null)
                    soTcpServer.Shutdown(SocketShutdown.Both);

            }

        }

        private static int Asc(string character)
        {
            // VB.NET ASC function
            if (character.Length == 1)
            {
                System.Text.ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
                int intAsciiCode = (int)asciiEncoding.GetBytes(character)[0];
                return (intAsciiCode);
            }
            else
            {
                throw new ApplicationException("Character is not valid.");
            }

        }

        #endregion

    }

}
